import { ITEM_HEIGHT, MAX_ITEM_COUNT } from './config';
import { update, updatePaddingSet } from './render';
import { getData } from './utils';

const $state = {};

//被代理的私有数据
const data = {
  dataSource: [], //所有数据
  currentData: [], //当前可视区域显示的数据
  startIndex: 0,
  endIndex: 0,
  paddingSet: {
    paddingTop: 0,
    paddingBottom: 0,
  },
};

/**
 * 该方法根据判断总数据的第(视口列表条数,8)项是否存在
 * @returns 如果存在返回索引8 如果不存在返回最后一个索引
 */
function _setEndIndex() {
  //访问startIndex并加上最大的列表项数量就可以拿到endIndex索引
  //*2 是两屏虚拟列表
  const endIndex = $state.startIndex + MAX_ITEM_COUNT * 2;

  // console.log(endIndex, $state.startIndex, MAX_ITEM_COUNT);
  //8 0 8

  //需要判断endIndex是否会超过dataSource的最后一项 如果存在返回endIndex 否则返回dataSource当前的最后一项
  return $state.dataSource[endIndex] ? endIndex : $state.dataSource.length - 1;
}

/**
 * 该函数给$state对象做响应式处理
 * @list 列表容器节点
 * @returns 返回代理后的$state对象
 */
export function reactive(list) {
  //代理给 $state 并将其返回外部使用
  Object.defineProperties($state, {
    dataSource: {
      get() {
        return data.dataSource;
      },
      set(newValue) {
        data.dataSource = newValue;

        //该属性被修改时做以下额外的程序操作
        //修改currentData里的数据
        setCurrentData();
      },
    },
    currentData: {
      get() {
        return data.currentData;
      },
      set(newValue) {
        data.currentData = newValue;

        //当被修改时并渲染视图
        update($state.currentData, list);
      },
    },
    startIndex: {
      get() {
        return data.startIndex;
      },
      set(newValue) {
        //优化：当startIndex的值被修改时 更新最新值 否则不做更改
        if ($state.startIndex !== newValue) {
          data.startIndex = newValue;
          //1.每次更改该值都需要重新改变currentData容器里的数据
          setCurrentData();

          //因为startIndex更改的同时endIndex也会同时改变
          //2.如果endIndex > 所有数组dataSource的最后一位时 要重新获取新的数据并放入dataSource里
          if ($state.endIndex >= $state.dataSource.length - 1) {
            //没有多余数据可供视图渲染了，需要重新请求新的数据
            //init 为 下一条数据 假如20条 下一条就是21
            //count 为 20条 * 2 即 设定为虚拟列表为40条
            setDataSource(
              $state.dataSource.length + 1,
              $state.dataSource.length * 2
            );
          }

          //3.还需要设置padding
          setPaddingSet();
        }
      },
    },
    endIndex: {
      get() {
        return _setEndIndex();
      },
    },
    paddingSet: {
      get() {
        return data.paddingSet;
      },
      set(newValue) {
        data.paddingSet = newValue;
        //更新视图上的padding
        updatePaddingSet($state.paddingSet, list);
      },
    },
  });

  return $state;
}

/**
 * 该方法会将$state.dataSource数据重新赋值为一个组合后的数组数据
 * @param {*} init 起始数据索引位置
 * @param {*} count 数据条数
 */
export function setDataSource(init, count) {
  //将原有的数据列表和新定义的数据列表合并重组成新的数组
  $state.dataSource = [...$state.dataSource, ...getData(init, count)];
}

/**
 * 设置当前可视区域容器数据的方法
 */
export function setCurrentData() {
  //重新计算startIndex
  const startIndex = resetStartIndex();

  //截取区间范围(从第一项到最后一项)的数组成员至当前可视区域容器里
  $state.currentData = $state.dataSource.slice(startIndex, $state.endIndex);
}

/**
 * 向上滑动时计算视图上padding的值
 */
export function setPaddingSet() {
  //重新计算startIndex
  const startIndex = resetStartIndex();

  $state.paddingSet = {
    //计算paddingTop的值 = 第一个索引 * 列表项高度
    //0 * itemHeight = 0
    //1 * itemHeight = 101 出现滚动条
    //...
    paddingTop: startIndex * ITEM_HEIGHT,
    //仍有许多不需要渲染的虚拟数据去计算paddingBottom的值
    //paddingBottom的值 = (整个数据长度 - endIndex) * 列表项高度
    paddingBottom: ($state.dataSource.length - $state.endIndex) * ITEM_HEIGHT,
  };
}

/**
 * 该方法会重置startIndex实现缓存2屏来优化白屏问题
 * @returns 重置后的startIndex
 */
export function resetStartIndex() {
  return $state.startIndex <= MAX_ITEM_COUNT
    ? 0
    : $state.startIndex - MAX_ITEM_COUNT;
}
